import SwiftCompilerPlugin
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

public struct LSLoggerMacro: MemberMacro {
    public static func expansion(
        of node: SwiftSyntax.AttributeSyntax,
        providingMembersOf declaration: some SwiftSyntax.DeclGroupSyntax,
        in _: some SwiftSyntaxMacros.MacroExpansionContext
    ) throws -> [SwiftSyntax.DeclSyntax] {
        guard let declarationName = declaration.as(ClassDeclSyntax.self)?.name.text ?? declaration.as(StructDeclSyntax.self)?.name.text ?? declaration.as(ActorDeclSyntax.self)?.name.text ?? declaration.as(EnumDeclSyntax.self)?.name.text else {
            throw Error.unknownDeclaration
        }

        let subsystem: String? = if case let .argumentList(arguments) = node.arguments {
            Array(arguments)
                .first(where: { $0.label?.text == "subsystem" })?
                .expression
                .as(StringLiteralExprSyntax.self)?
                .representedLiteralValue
        } else {
            nil
        }

        let category: String? = if case let .argumentList(arguments) = node.arguments {
            Array(arguments)
                .first(where: { $0.label?.text == "category" })?
                .expression
                .as(StringLiteralExprSyntax.self)?
                .representedLiteralValue
        } else {
            nil
        }

        let syntaxNodeString: String
        if let subsystem {
            syntaxNodeString = "static let logger = LSLogger(subsystem: \"\(subsystem)\", category: \"\(category != nil ? category! : declarationName)\")"
        } else {
            syntaxNodeString = "static let logger = LSLogger(category: \"\(category != nil ? category! : declarationName)\")"
        }

        return try [DeclSyntax(VariableDeclSyntax(.init(stringLiteral: syntaxNodeString)))]
    }
}

extension LSLoggerMacro {
    enum Error: Swift.Error, CustomStringConvertible {
        case unknownDeclaration

        // MARK: Internal

        var description: String {
            switch self {
            case .unknownDeclaration:
                "LSLogger must be used on a class, struct, actor, or enum"
            }
        }
    }
}

@main
struct LSLoggerMacroPlugin: CompilerPlugin {
    let providingMacros: [Macro.Type] = [
        LSLoggerMacro.self,
    ]
}
